<?php
/**
 * @file 3gplex/includes/user.inc
 * 
 * User core module configuration API.
 */

require_once('./'. drupal_get_path('module', 'drupalplex') .'/includes/module.inc');

define("PUSER_NO_NAME", "Can't set some permissions for a role:
	There is no role defined to set permissions for");

define("PUSER_NO_PERM", "Can't set permissions for role %role:
	Permission(s) %perm do(es)n't exist");

class plex_user_exception extends plex_exception
{
	function __construct($message, $role, $perm)
	{
		$this->message = $message;
		$this->arguments = array(
			'%role'=>$role,
			'%perm'=>$perm
		);
	}
}

/**
 * User core module wrapper.
 */
class plex_user extends plex_module
{
	const description = "Configures and loads API for Drupal core user module: Users, Roles and Permissions of your Drupal web site";
	function get_description() { return self::description; }
	
	static $permissions = array();

	protected function build_settings_xml(SimpleXMLElement $xml)
	{
		$settings = array();
		foreach ($xml->role as $role) {
			$setting = array('role' => (string)$role->name);

			if (isset($role->set)) {
				$setting['set'] = array();
				foreach ((array)$role->set as $set) {
					$setting['set'][] = (string)$set;
				}
			}
			if (isset($role->add)) {
				$setting['add'] = array();
				foreach ((array)$role->add as $add) {
					$setting['add'][] = (string)$add;
				}
			}
			if (isset($role->rem)) {
				$setting['rem'] = array();
				foreach ((array)$role->rem as $rem) {
					$setting['rem'][] = (string)$rem;
				}
			}
			$settings[] = $setting;
		}
		return $settings;
	}

	/**
	 * @note We add roles as they're named.
	 */
	protected function validate_settings(array &$settings)
	{
		if (empty(self::$permissions)) self::$permissions = module_invoke_all('perm');

		reset($settings);
		while (list($key, $val) = each($settings)) {
			$settings[$key]['role'] = trim($val['role']);
			$settings[$key]['set'] = (array)$val['set'];
			$settings[$key]['add'] = (array)$val['add'];
			$settings[$key]['rem'] = (array)$val['rem'];
			$val = $settings[$key];
			if (strlen($val['role']) < 1) {
				throw new plex_exception(PUSER_NO_NAME);
			}
			$permissions = array_merge($val['set'], $val['add'], $val['rem']);
			$invalids = array_diff($permissions, self::$permissions);
			if (!empty($invalids)) {
				throw new plex_user_exception($val['role'], implode(', ', $invalids));
			}
			/*if (!$this->role_exists($val['role'])) {
				plex::warn("Role %role does not exists and impossible to add it", array('%role'=>$role));
				unset($settings[$key]);
			}*/
		}
	}

	/**
	 * Configure @c user core module given an array of settings.
	 */
	protected function apply_settings(array &$settings)
	{
		foreach ($settings as $setting) {
			if (!$this->role_exists($setting['role'])) $this->add_role($setting['role']);
			if (!empty($setting['set'])) $this->set_perm($setting['role'], $setting['set']);
			if (!empty($setting['add'])) $this->add_perm($setting['role'], $setting['add']);
			if (!empty($setting['rem'])) $this->rem_perm($setting['role'], $setting['rem']);
		}
		return true;
	}

	protected function revert_settings(array $settings)
	{
		foreach ($settings as $setting) {
			switch ($setting['role']) {
			case 'anonymous user':
			case 'authenticated user':
			break;
			default:
			$this->rem_role($setting['role']);
			}
		}
		return true;
	}

	/**
	 * Returns the rid for role $role if existing, boolean false otherwise.
	 */
	function role2rid($name)
	{
		$rid = false;
		$res = db_query("select rid from {role} where name='%s'", $name);
		if ($res !== false) $rid = db_result($res);
		return $rid;
	}
	
	/**
	 * Returns true if role $name exists, false otherwise.
	 */
	function role_exists($name)
	{
		if (db_result(db_query(
			"select count(*) from {role} where name = '%s'", $name))) {
			return true;
		}
		return false;
	}

	/**
	 * Creates role $name.
	 */
	function add_role($name)
	{
		user_admin_role_submit(42, array('name'=>$name, 'op'=>t('Add role')));
	}
	
	/**
	 * Deletes role $name.
	 */
	function rem_role($name)
	{
		$rid = ucrole_rid($name);
		if ($rid !== false) {
			user_admin_role_submit(42, array('rid'=>$rid, 'op'=>t('Delete role')));
		}
	}
	
	/**
	 * Add the set of permissions in $perm to given $role.
	 */
	function add_perm($role, $perm)
	{
		$perm = (array)$perm;
		$rid = $this->role2rid($role);
		if ($rid === false) return;
		$res = db_query("select perm from {permission} where rid = %d", $rid);
		if ($res === false) return;
		$perms = db_result($res);
		if ($perms == false || $perms == "") {
			$perms = array();
		} else {
			$perms = explode(', ', $perms);
		}
		$perms = array_unique(array_merge($perms, $perm));
		$this->set_perm($role, $perms);
	}

	/**
	 * Remove the set of permissions in $perm to given $role.
	 */
	function rem_perm($role, $perm)
	{
		$perm = (array)$perm;
		$rid = $this->role2rid($role);
		if ($rid === false) return;
		$res = db_query("select perm from {permission} where rid = %d", $rid);
		if ($res === false) return;
		$perms = db_result($res);
		if ($perms == false || $perms == "") return;
		$perms = explode(', ', $perms);
		$perms = array_diff($perms, $perm);
		$this->set_perm($role, $perms);
	}
	
	/**
	 * Set permissions for given $role to be the exact set in $perm.
	 */
	function set_perm($role, $perm)
	{
		$perm = (array)$perm;
		$rid = $this->role2rid($role);
		if ($rid === false) return;
		db_query("delete from {permission} where rid=%d", $rid);
		db_query("insert into {permission} (rid, perm) values (%d, '%s')", $rid, implode(', ', $perm));
	}
}

/**
 * @page plex_user_page User core module configuration formats
 * 
 * The @c user core module in Drupal handles 
 * 
 * @section Drupal Terminology
 * 
 * @li users http://drupal.org/node/937#user
 * @li roles http://drupal.org/getting-started/before/terminology#roles
 * @li permissions http://drupal.org/getting-started/before/terminology#permissions
 */
